#ifndef __SCENE_NODE_H__
#define __SCENE_NODE_H__

#include "ControlledObject.h"
#include "BoundingVolume.h"

namespace Nezha
{

	enum SceneObjectUpdate
	{
		SOU_WORLDTRANSFORM = 0x1,
		SOU_WORLDTRANSFORM_MATRIX = 0x2,
		SOU_CHILDREN = 0x4,
		//SOU_PARENT_NOTIFIED = 0x20,

		SOU_BOUND_GEOMETRY = 0x100,
		SOU_BOUND_UPWARD = 0x200,
		SOU_BOUND_DOWNWARD = 0x400,

		SOU_CONTROLLER = 0x10000,
	};

	const u32 TransformUpdateBits = 0xFF;
	const u32 BoundUpdateBits = 0xFF00;

#define DEFAULT_SCENE_LAYER 0x1

	class SceneGraph;
	class RenderObject;
	class RenderTarget;
	class SceneNode;

	class NEZHA_ENGINE_ENTRY CustomCullingProc
	{
	public:
		virtual ~CustomCullingProc(){}

		virtual bool isSceneNodeVisible(SceneNode* sn, RenderTarget* curRT) = 0;
	};

	/** Scene transform & Hierarchy & spatial partition.
	@REMARKS
		Scene node should have no render object.
	*/
	class NEZHA_ENGINE_ENTRY SceneNode
	{
	public:
		virtual ~SceneNode();

		virtual int getSceneObjectType() const
		{
			return SOT_NODE;
		}

		const Vector3& getPosition() const;
		const Vector3& getScale() const;
		const Quaternion& getOrientation() const;

		const Vector3& getWorldPos() const;
		const Vector3& getWorldScale() const;
		const Quaternion& getWorldOrientation() const;

		void setPosition(const Vector3& pos);
		void setOrientation(const Quaternion& q);
		void setScale(const Vector3& scale);
		void translate(const Vector3& trans, bool localSpace = true);
		void rotate(const Matrix3x3& rotMat, bool localSpace = true);
		void rotate(const Quaternion& quat, bool localSpace = true);

		const Matrix4x4& getWorldTransform() const;

		u32 getTransformNBoundUpdateFlag() const;
		bool getTransformDirty() const;

		// update transform & bounds immediately.
		void updateTransformNBounds(float dt, u32 flag = TransformUpdateBits);

		const AABB& getWorldAABB(u32 updateFlag = SOU_BOUND_GEOMETRY) const;

		//void update(u32 filter = 0);

		u32 getSceneLayer() const;

		// hierarchy
		u32 getNumChildren() const;
		SceneNode* getChild(u32 i);
		SceneNode* getChild(const _string& name);
		void attachChild(SceneNode* child);
		void removeChild(SceneNode* child);
		void removeChild(const _string& name);
		void removeAllChild();

		SceneNode(const _string& name);

		SceneNode* getParentSceneNode();

		void setCullingMask(u32 cullingMask);

		u32 getCullingMask() const;

		// TODO culling & partition

		virtual void addToRenderQueue() {}

		virtual void update(float dt);

		u16 getLOD() const;

		void setLOD(u16 lod);

		const _string& getName() const
		{
			return mName;
		}

		void setTransformNBoundUpdateFlag(u32 flag);

		ContollerJoint* getControllerJoint(bool createIfHasNot = false);

		void setCustomCulling(CustomCullingProc* c);

		CustomCullingProc* getCustomCulling();
	private:
		_string mName;

	protected:
		virtual void updateGeometryBound(AABB& aabb) const;
		virtual bool preAttachChild(SceneNode* t);
		virtual void onAttachChild(SceneNode* t);
		virtual void onRemoveChild(SceneNode* t);

		void updateTransformFromParent() const;
		void upwardSetBoundsDirty() const;

		// transform
		// local space
		Vector3 mPosition;
		Vector3 mScale;
		Quaternion mOrientation;

		// world space
		mutable Vector3 mWorldPos;
		mutable Vector3 mWorldScale;
		mutable Quaternion mWorldOrientation;
		mutable Matrix4x4 mHMatrixL2W;
		mutable u32 mTransformNBoundUpdateFlag;

		u32 mCullingMask;
		u32 mSceneLayer;

		typedef PArray<SceneNode*> SceneNodeList;
		SceneNodeList mChildren;
		SceneNode* mParentSceneNode;

		// create only when needed.
		ContollerJoint* mControllerJoint;
		CustomCullingProc* mCustomCulling;

		mutable AABB mWorldAABB;

		u16 mLod;
	};

	inline u32 SceneNode::getTransformNBoundUpdateFlag() const
	{
		return mTransformNBoundUpdateFlag;
	}

	inline bool SceneNode::getTransformDirty() const
	{
		return NZTestFlag(mTransformNBoundUpdateFlag, SOU_WORLDTRANSFORM);
	}

	inline const Vector3& SceneNode::getPosition() const
	{
		return mPosition;
	}

	inline const Vector3& SceneNode::getScale() const
	{
		return mScale;
	}

	inline const Quaternion& SceneNode::getOrientation() const
	{
		return mOrientation;
	}

	inline u32 SceneNode::getNumChildren() const
	{
		return mChildren.size();
	}

	inline u32 SceneNode::getSceneLayer() const
	{
		return mSceneLayer;
	}

	inline void SceneNode::setCullingMask(u32 cullingMask)
	{
		mCullingMask = cullingMask;
	}

	inline u32 SceneNode::getCullingMask() const
	{
		return mCullingMask;
	}

	inline SceneNode* SceneNode::getParentSceneNode()
	{
		return mParentSceneNode;
	}

	inline u16 SceneNode::getLOD() const
	{
		return mLod;
	}

	inline void SceneNode::setLOD(u16 lod)
	{
		mLod = lod;
	}

	inline void SceneNode::setTransformNBoundUpdateFlag(u32 flag)
	{
		mTransformNBoundUpdateFlag |= flag;
	}

	inline void SceneNode::setCustomCulling(CustomCullingProc* c)
	{
		mCustomCulling = c;
	}

	inline CustomCullingProc* SceneNode::getCustomCulling()
	{
		return mCustomCulling;
	}

}//end namespace Nezha

#endif //end __SCENE_NODE_H__